From 83e5fb7a8e6fe31c5f164b1b69e4b4fd85283102 Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Wed, 5 Nov 2003 23:22:13 +0000 Subject: [PATCH] bitkeeper revision 1.567 (3fa98625j0d47oe7ZCKLDdbnYED8wA) xi_restore_linux.c, xi_save_linux.c: new file dom0_ops.c, Makefile: Starting DOM0 support for suspend/resume. Untested so far, and resume is currently incomplete. --- .rootkeys | 2 + tools/internal/Makefile | 2 +- tools/internal/xi_restore_linux.c | 346 ++++++++++++++++++++++++++++ tools/internal/xi_save_linux.c | 365 ++++++++++++++++++++++++++++++ xen/common/dom0_ops.c | 36 +-- 5 files changed, 737 insertions(+), 14 deletions(-) create mode 100644 tools/internal/xi_restore_linux.c create mode 100644 tools/internal/xi_save_linux.c diff --git a/.rootkeys b/.rootkeys index 5d4c02c8f7..f12c77063a 100644 --- a/.rootkeys +++ b/.rootkeys @@ -169,6 +169,8 @@ 3ec43c5dmQxGDvgJJXbV1yLxT30Y1A tools/internal/xi_helper 3f108ad5wQm0ZaQ4GXFoUhH1W1aW9w tools/internal/xi_list.c 3f0458aaXhD8BQAggO81gv30RQ-ifA tools/internal/xi_phys_grant.c +3fa9861aBdNV1yCjfY4cLPr4Mtrpuw tools/internal/xi_restore_linux.c +3fa98615LWZfagwDBp7XfuC-u9wi3w tools/internal/xi_save_linux.c 3f108adb2b5OkKL6-faG3lMiOYDf_w tools/internal/xi_sched_domain.c 3f108ade1v8weyh1sKx890VTd240Hw tools/internal/xi_sched_global.c 3eb781fd8oRfPgH7qTh7xvgmwD6NgA tools/internal/xi_start.c diff --git a/tools/internal/Makefile b/tools/internal/Makefile index c8b8dcffaf..83e23d2aa3 100644 --- a/tools/internal/Makefile +++ b/tools/internal/Makefile @@ -8,7 +8,7 @@ SRCS = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SRCS)) TARGETS = xi_create xi_start xi_stop xi_destroy xi_build -TARGETS += xi_phys_grant xi_list +TARGETS += xi_phys_grant xi_list xi_save_linux xi_restore_linux TARGETS += xi_sched_global xi_sched_domain xi_usage xi_vif_params INSTALL = $(TARGETS) xi_vifinit xi_helper diff --git a/tools/internal/xi_restore_linux.c b/tools/internal/xi_restore_linux.c new file mode 100644 index 0000000000..90ec98ab9d --- /dev/null +++ b/tools/internal/xi_restore_linux.c @@ -0,0 +1,346 @@ +/****************************************************************************** + * xi_restore_linux.c + * + * Restore the state of a Xenolinux session. + * + * Copyright (c) 2003, K A Fraser. + */ + +#include "dom0_defs.h" +#include "mem_defs.h" +#include + +static char *argv0 = "internal_save_linux"; + +/* A table mapping each PFN to its current MFN. */ +static unsigned long *pfn_to_mfn_table; +/* A table mapping each current MFN to its canonical PFN. */ +static unsigned long *mfn_to_pfn_table; + +static int devmem_fd; + +static int init_pfn_mapper(void) +{ + if ( (devmem_fd = open("/dev/mem", O_RDWR)) < 0 ) + { + PERROR("Could not open /dev/mem"); + return -1; + } + return 0; +} + +static void *map_pfn(unsigned long pfn) +{ + void *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, + MAP_SHARED, devmem_fd, pfn << PAGE_SHIFT); + if ( vaddr == MAP_FAILED ) + { + PERROR("Could not mmap a domain pfn using /dev/mem"); + return NULL; + } + return vaddr; +} + +static void unmap_pfn(void *vaddr) +{ + (void)munmap(vaddr, PAGE_SIZE); +} + +/* + * Returns TRUE if the given machine frame number has a unique mapping + * in the guest's pseudophysical map. + */ +#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \ + (((_mfn) < (1024*1024)) && \ + (pfn_to_mfn_table[mfn_to_pfn_table[_mfn]] == (_mfn))) + +/* Returns TRUE if MFN is successfully converted to a PFN. */ +static int translate_mfn_to_pfn(unsigned long *pmfn) +{ + unsigned long mfn = *pmfn; + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + return 0; + *pmfn = mfn_to_pfn_table[mfn]; + return 1; +} + +static int check_pfn_ownership(unsigned long mfn, unsigned int dom) +{ + dom0_op_t op; + op.cmd = DOM0_GETPAGEFRAMEINFO; + op.u.getpageframeinfo.pfn = mfn; + if ( (do_dom0_op(&op) < 0) || (op.u.getpageframeinfo.domain != dom) ) + return 0; + return 1; +} + +static unsigned int get_pfn_type(unsigned long mfn) +{ + dom0_op_t op; + op.cmd = DOM0_GETPAGEFRAMEINFO; + op.u.getpageframeinfo.pfn = mfn; + if ( do_dom0_op(&op) < 0 ) + { + PERROR("Unexpected failure when getting page frame info!"); + exit(1); + } + return op.u.getpageframeinfo.type; +} + +static int checked_read(int fd, void *buf, size_t count) +{ + int rc; + while ( ((rc = read(fd, buf, count)) == -1) && (errno == EINTR) ) + continue; + return rc == count; +} + +int main(int argc, char **argv) +{ + dom0_op_t op; + int rc = 1, i; + unsigned long mfn, dom = 0; + + /* Number of page frames in use by this XenoLinux session. */ + unsigned long nr_pfns; + + /* A copy of the CPU context of the guest. */ + full_execution_context_t ctxt; + + /* First 16 bytes of the state file must contain 'XenoLinuxSuspend'. */ + char signature[16]; + + /* A copy of the domain's name. */ + char name[MAX_DOMAIN_NAME]; + + /* A table containg the type of each PFN (/not/ MFN!). */ + unsigned long *pfn_type; + + /* A temporary mapping, and a copy, of one frame of guest memory. */ + unsigned long *ppage, page[1024]; + + /* A copy of the pfn-to-mfn table frame list. */ + unsigned long pfn_to_mfn_frame_list[1024]; + /* A temporary mapping of one frame in the above list. */ + unsigned long *pfn_to_mfn_frame; + + /* A temporary mapping, and a copy, of the guest's suspend record. */ + suspend_record_t *p_srec, srec; + + /* The name and descriptor of the file that we are reading from. */ + char *filename; + int fd; + + if ( argv[0] != NULL ) + argv0 = argv[0]; + + if ( argc != 2 ) + { + fprintf(stderr, "Usage: %s \n", argv0); + return 1; + } + + filename = argv[1]; + if ( (fd = open(name, O_RDONLY)) == -1 ) + { + PERROR("Could not open file for writing"); + return 1; + } + + /* Start writing out the saved-domain record. */ + if ( !checked_read(fd, signature, 16) || + (memcmp(signature, "XenoLinuxSuspend", 16) != 0) ) + { + ERROR("Unrecognised state format -- no signature found"); + goto out; + } + + if ( !checked_read(fd, name, sizeof(name)) || + !checked_read(fd, &nr_pfns, sizeof(unsigned long)) || + !checked_read(fd, &ctxt, sizeof(ctxt)) || + !checked_read(fd, pfn_to_mfn_frame_list, PAGE_SIZE) ) + { + ERROR("Error when reading from state file"); + goto out; + } + + if ( nr_pfns > 1024*1024 ) + { + ERROR("Invalid state file -- pfn count out of range"); + goto out; + } + + for ( i = 0; i < MAX_DOMAIN_NAME; i++ ) + { + if ( name[i] == '\0' ) break; + if ( name[i] & 0x80 ) + { + ERROR("Random characters in domain name"); + goto out; + } + } + name[MAX_DOMAIN_NAME-1] = '\0'; + + /* We want zeroed memory so use calloc rather than malloc. */ + mfn_to_pfn_table = calloc(1, 4 * 1024 * 1024); + pfn_to_mfn_table = calloc(1, 4 * nr_pfns); + pfn_type = calloc(1, 4 * nr_pfns); + + if ( !checked_read(fd, pfn_type, 4 * nr_pfns) ) + { + ERROR("Error when reading from state file"); + goto out; + } + + /* Create a new domain of teh appropriate size, and find it's dom_id. */ + op.cmd = DOM0_CREATEDOMAIN; + op.u.createdomain.memory_kb = nr_pfns * (PAGE_SIZE / 1024); + memcpy(op.u.createdomain.name, name, MAX_DOMAIN_NAME); + if ( do_dom0_op(&op) < 0 ) + { + ERROR("Could not create new domain"); + goto out; + } + dom = op.u.createdomain.domain; + + if ( init_pfn_mapper() < 0 ) + goto out; + + /* Is the suspend-record MFN actually valid for this domain? */ + if ( !check_pfn_ownership(ctxt.i386_ctxt.esi, dom) ) + { + ERROR("Invalid state record pointer"); + goto out; + } + + /* If the suspend-record MFN is okay then grab a copy of it to @srec. */ + p_srec = map_pfn(ctxt.i386_ctxt.esi); + memcpy(&srec, p_srec, sizeof(srec)); + unmap_pfn(p_srec); + + if ( !check_pfn_ownership(srec.pfn_to_mfn_frame_list, dom) ) + { + ERROR("Invalid pfn-to-mfn frame list pointer"); + goto out; + } + + /* + * Construct the local pfn-to-mfn and mfn-to-pfn tables. On exit from this + * loop we have each MFN mapped at most once. Note that there may be MFNs + * that aren't mapped at all: we detect these by MFN_IS_IN_PSEUDOPHYS_MAP. + */ + pfn_to_mfn_frame = NULL; + for ( i = 0; i < srec.nr_pfns; i++ ) + { + /* Each frameful of table frames must be checked & mapped on demand. */ + if ( (i & 1023) == 0 ) + { + mfn = pfn_to_mfn_frame_list[i/1024]; + if ( !check_pfn_ownership(mfn, dom) ) + { + ERROR("Invalid frame number if pfn-to-mfn frame list"); + goto out; + } + if ( pfn_to_mfn_frame != NULL ) + unmap_pfn(pfn_to_mfn_frame); + pfn_to_mfn_frame = map_pfn(mfn); + } + + mfn = pfn_to_mfn_frame[i & 1023]; + + if ( !check_pfn_ownership(mfn, dom) ) + { + ERROR("Invalid frame specified with pfn-to-mfn table"); + goto out; + } + + pfn_to_mfn_table[i] = mfn; + + /* Did we map this MFN already? That would be invalid! */ + if ( MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + ERROR("A machine frame appears twice in pseudophys space"); + goto out; + } + + mfn_to_pfn_table[mfn] = i; + + /* Query page type by MFN, but store it by PFN. */ + pfn_type[i] = get_pfn_type(mfn); + } + + /* Canonicalise the suspend-record frame number. */ + if ( !translate_mfn_to_pfn(&ctxt.i386_ctxt.esi) ) + { + ERROR("State record is not in range of pseudophys map"); + goto out; + } + + /* Canonicalise each GDT frame number. */ + for ( i = 0; i < ctxt.gdt_ents; i += 512 ) + { + if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) ) + { + ERROR("GDT frame is not in range of pseudophys map"); + goto out; + } + } + + /* Canonicalise the page table base pointer. */ + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) ) + { + ERROR("PT base is not in range of pseudophys map"); + goto out; + } + ctxt.pt_base = mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << PAGE_SHIFT; + + /* Canonicalise the pfn-to-mfn table frame-number list. */ + for ( i = 0; i < srec.nr_pfns; i += 1024 ) + { + if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ) + { + ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys"); + goto out; + } + } + + /* Now write out each data page, canonicalising page tables as we go... */ + for ( i = 0; i < srec.nr_pfns; i++ ) + { + mfn = pfn_to_mfn_table[i]; + ppage = map_pfn(mfn); + memcpy(&page, ppage, PAGE_SIZE); + unmap_pfn(ppage); + if ( (pfn_type[i] == L1TAB) || (pfn_type[i] == L2TAB) ) + { + for ( i = 0; i < 1024; i++ ) + { + if ( !(page[i] & _PAGE_PRESENT) ) continue; + mfn = page[i] >> PAGE_SHIFT; + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + ERROR("Frame number in pagetable page is invalid"); + goto out; + } + page[i] &= PAGE_SIZE - 1; + page[i] |= mfn_to_pfn_table[mfn] << PAGE_SHIFT; + } + } + write(fd, &page, PAGE_SIZE); + } + + /* Success! */ + rc = 0; + + out: + /* If we experience an error then kill the half-constructed domain. */ + if ( (rc != 0) && (dom != 0) ) + { + op.cmd = DOM0_DESTROYDOMAIN; + op.u.destroydomain.domain = dom; + op.u.destroydomain.force = 1; + (void)do_dom0_op(&op); + } + + return !!rc; +} diff --git a/tools/internal/xi_save_linux.c b/tools/internal/xi_save_linux.c new file mode 100644 index 0000000000..5162ae9d26 --- /dev/null +++ b/tools/internal/xi_save_linux.c @@ -0,0 +1,365 @@ +/****************************************************************************** + * xi_save_linux.c + * + * Save the state of a running Xenolinux session. + * + * Copyright (c) 2003, K A Fraser. + */ + +#include "dom0_defs.h" +#include "mem_defs.h" +#include + +static char *argv0 = "internal_save_linux"; + +/* A table mapping each PFN to its current MFN. */ +static unsigned long *pfn_to_mfn_table; +/* A table mapping each current MFN to its canonical PFN. */ +static unsigned long *mfn_to_pfn_table; + +static int devmem_fd; + +static int init_pfn_mapper(void) +{ + if ( (devmem_fd = open("/dev/mem", O_RDWR)) < 0 ) + { + PERROR("Could not open /dev/mem"); + return -1; + } + return 0; +} + +static void *map_pfn(unsigned long pfn) +{ + void *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, + MAP_SHARED, devmem_fd, pfn << PAGE_SHIFT); + if ( vaddr == MAP_FAILED ) + { + PERROR("Could not mmap a domain pfn using /dev/mem"); + return NULL; + } + return vaddr; +} + +static void unmap_pfn(void *vaddr) +{ + (void)munmap(vaddr, PAGE_SIZE); +} + +/* + * Returns TRUE if the given machine frame number has a unique mapping + * in the guest's pseudophysical map. + */ +#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \ + (((_mfn) < (1024*1024)) && \ + (pfn_to_mfn_table[mfn_to_pfn_table[_mfn]] == (_mfn))) + +/* Returns TRUE if MFN is successfully converted to a PFN. */ +static int translate_mfn_to_pfn(unsigned long *pmfn) +{ + unsigned long mfn = *pmfn; + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + return 0; + *pmfn = mfn_to_pfn_table[mfn]; + return 1; +} + +static int check_pfn_ownership(unsigned long mfn, unsigned int dom) +{ + dom0_op_t op; + op.cmd = DOM0_GETPAGEFRAMEINFO; + op.u.getpageframeinfo.pfn = mfn; + if ( (do_dom0_op(&op) < 0) || (op.u.getpageframeinfo.domain != dom) ) + return 0; + return 1; +} + +static unsigned int get_pfn_type(unsigned long mfn) +{ + dom0_op_t op; + op.cmd = DOM0_GETPAGEFRAMEINFO; + op.u.getpageframeinfo.pfn = mfn; + if ( do_dom0_op(&op) < 0 ) + { + PERROR("Unexpected failure when getting page frame info!"); + exit(1); + } + return op.u.getpageframeinfo.type; +} + +static int checked_write(int fd, const void *buf, size_t count) +{ + int rc; + while ( ((rc = write(fd, buf, count)) == -1) && (errno = EINTR) ) + continue; + return rc == count; +} + +int main(int argc, char **argv) +{ + dom0_op_t op; + int rc = 1, i; + unsigned long mfn, dom; + + /* Remember if we stopped the guest, so we can restart it on exit. */ + int we_stopped_it = 0; + + /* A copy of the CPU context of the guest. */ + full_execution_context_t ctxt; + + /* A copy of the domain's name. */ + char name[MAX_DOMAIN_NAME]; + + /* A table containg the type of each PFN (/not/ MFN!). */ + unsigned long *pfn_type; + + /* A temporary mapping, and a copy, of one frame of guest memory. */ + unsigned long *ppage, page[1024]; + + /* A temporary mapping, and a copy, of the pfn-to-mfn table frame list. */ + unsigned long *p_pfn_to_mfn_frame_list, pfn_to_mfn_frame_list[1024]; + /* A temporary mapping of one frame in the above list. */ + unsigned long *pfn_to_mfn_frame; + + /* A temporary mapping, and a copy, of the guest's suspend record. */ + suspend_record_t *p_srec, srec; + + /* The name and descriptor of the file that we are writing to. */ + char *filename; + int fd; + + if ( argv[0] != NULL ) + argv0 = argv[0]; + + if ( argc != 3 ) + { + fprintf(stderr, "Usage: %s \n", argv0); + return 1; + } + + dom = atoi(argv[1]); + if ( dom == 0 ) + { + ERROR("Did you really mean domain 0?"); + return 1; + } + + filename = argv[2]; + if ( (fd = open(name, O_CREAT|O_EXCL|O_RDWR)) == -1 ) + { + PERROR("Could not open file for writing"); + return 1; + } + + /* Ensure that the domain exists, and that it is stopped. */ + for ( ; ; ) + { + op.cmd = DOM0_GETDOMAININFO; + op.u.getdomaininfo.domain = dom; + if ( (do_dom0_op(&op) < 0) || (op.u.getdomaininfo.domain != dom) ) + { + PERROR("Could not get info on domain"); + goto out; + } + + memcpy(&ctxt, &op.u.getdomaininfo.ctxt, sizeof(ctxt)); + memcpy(name, op.u.getdomaininfo.name, sizeof(name)); + + if ( op.u.getdomaininfo.state == DOMSTATE_STOPPED ) + break; + + we_stopped_it = 1; + + op.cmd = DOM0_STOPDOMAIN; + op.u.stopdomain.domain = dom; + (void)do_dom0_op(&op); + + sleep(1); + } + + /* A cheesy test to see whether the domain contains valid state. */ + if ( ctxt.pt_base == 0 ) + { + ERROR("Domain is not in a valid Xenolinux state"); + goto out; + } + + if ( init_pfn_mapper() < 0 ) + goto out; + + /* Is the suspend-record MFN actually valid for this domain? */ + if ( !check_pfn_ownership(ctxt.i386_ctxt.esi, dom) ) + { + ERROR("Invalid state record pointer"); + goto out; + } + + /* If the suspend-record MFN is okay then grab a copy of it to @srec. */ + p_srec = map_pfn(ctxt.i386_ctxt.esi); + memcpy(&srec, p_srec, sizeof(srec)); + unmap_pfn(p_srec); + + if ( srec.nr_pfns > 1024*1024 ) + { + ERROR("Invalid state record -- pfn count out of range"); + goto out; + } + + if ( !check_pfn_ownership(srec.pfn_to_mfn_frame_list, dom) ) + { + ERROR("Invalid pfn-to-mfn frame list pointer"); + goto out; + } + + /* Grab a copy of the pfn-to-mfn table frame list. */ + p_pfn_to_mfn_frame_list = map_pfn(srec.pfn_to_mfn_frame_list); + memcpy(pfn_to_mfn_frame_list, p_pfn_to_mfn_frame_list, PAGE_SIZE); + unmap_pfn(p_pfn_to_mfn_frame_list); + + /* We want zeroed memory so use calloc rather than malloc. */ + mfn_to_pfn_table = calloc(1, 4 * 1024 * 1024); + pfn_to_mfn_table = calloc(1, 4 * srec.nr_pfns); + pfn_type = calloc(1, 4 * srec.nr_pfns); + + /* + * Construct the local pfn-to-mfn and mfn-to-pfn tables. On exit from this + * loop we have each MFN mapped at most once. Note that there may be MFNs + * that aren't mapped at all: we detect these by MFN_IS_IN_PSEUDOPHYS_MAP. + */ + pfn_to_mfn_frame = NULL; + for ( i = 0; i < srec.nr_pfns; i++ ) + { + /* Each frameful of table frames must be checked & mapped on demand. */ + if ( (i & 1023) == 0 ) + { + mfn = pfn_to_mfn_frame_list[i/1024]; + if ( !check_pfn_ownership(mfn, dom) ) + { + ERROR("Invalid frame number if pfn-to-mfn frame list"); + goto out; + } + if ( pfn_to_mfn_frame != NULL ) + unmap_pfn(pfn_to_mfn_frame); + pfn_to_mfn_frame = map_pfn(mfn); + } + + mfn = pfn_to_mfn_frame[i & 1023]; + + if ( !check_pfn_ownership(mfn, dom) ) + { + ERROR("Invalid frame specified with pfn-to-mfn table"); + goto out; + } + + pfn_to_mfn_table[i] = mfn; + + /* Did we map this MFN already? That would be invalid! */ + if ( MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + ERROR("A machine frame appears twice in pseudophys space"); + goto out; + } + + mfn_to_pfn_table[mfn] = i; + + /* Query page type by MFN, but store it by PFN. */ + pfn_type[i] = get_pfn_type(mfn); + } + + /* Canonicalise the suspend-record frame number. */ + if ( !translate_mfn_to_pfn(&ctxt.i386_ctxt.esi) ) + { + ERROR("State record is not in range of pseudophys map"); + goto out; + } + + /* Canonicalise each GDT frame number. */ + for ( i = 0; i < ctxt.gdt_ents; i += 512 ) + { + if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) ) + { + ERROR("GDT frame is not in range of pseudophys map"); + goto out; + } + } + + /* Canonicalise the page table base pointer. */ + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) ) + { + ERROR("PT base is not in range of pseudophys map"); + goto out; + } + ctxt.pt_base = mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << PAGE_SHIFT; + + /* Canonicalise the pfn-to-mfn table frame-number list. */ + for ( i = 0; i < srec.nr_pfns; i += 1024 ) + { + if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ) + { + ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys"); + goto out; + } + } + + /* Start writing out the saved-domain record. */ + if ( !checked_write(fd, "XenoLinuxSuspend", 16) || + !checked_write(fd, name, sizeof(name)) || + !checked_write(fd, &srec.nr_pfns, sizeof(unsigned long)) || + !checked_write(fd, &ctxt, sizeof(ctxt)) || + !checked_write(fd, pfn_to_mfn_frame_list, PAGE_SIZE) || + !checked_write(fd, pfn_type, 4 * srec.nr_pfns) ) + { + ERROR("Error when writing to state file"); + goto out; + } + + /* Now write out each data page, canonicalising page tables as we go... */ + for ( i = 0; i < srec.nr_pfns; i++ ) + { + mfn = pfn_to_mfn_table[i]; + + ppage = map_pfn(mfn); + memcpy(&page, ppage, PAGE_SIZE); + unmap_pfn(ppage); + + if ( (pfn_type[i] == L1TAB) || (pfn_type[i] == L2TAB) ) + { + for ( i = 0; i < 1024; i++ ) + { + if ( !(page[i] & _PAGE_PRESENT) ) continue; + mfn = page[i] >> PAGE_SHIFT; + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) ) + { + ERROR("Frame number in pagetable page is invalid"); + goto out; + } + page[i] &= PAGE_SIZE - 1; + page[i] |= mfn_to_pfn_table[mfn] << PAGE_SHIFT; + } + } + + if ( !checked_write(fd, &page, PAGE_SIZE) ) + { + ERROR("Error when writing to state file"); + goto out; + } + } + + /* Success! */ + rc = 0; + + out: + /* Restart the domain if we had to stop it to save its state. */ + if ( we_stopped_it ) + { + op.cmd = DOM0_STARTDOMAIN; + op.u.startdomain.domain = dom; + (void)do_dom0_op(&op); + } + + /* On error, make sure the file is deleted. */ + if ( rc != 0 ) + unlink(filename); + + return !!rc; +} diff --git a/xen/common/dom0_ops.c b/xen/common/dom0_ops.c index aad13c5626..5c2b47aa0d 100644 --- a/xen/common/dom0_ops.c +++ b/xen/common/dom0_ops.c @@ -330,24 +330,34 @@ long do_dom0_op(dom0_op_t *u_dom0_op) case DOM0_GETPAGEFRAMEINFO: { - struct pfn_info *page = frame_table + op.u.getpageframeinfo.pfn; + struct pfn_info *page; + unsigned long pfn = op.u.getpageframeinfo.pfn; - op.u.getpageframeinfo.domain = page->flags & PG_domain_mask; - op.u.getpageframeinfo.type = NONE; - if ( page->type_count & REFCNT_PIN_BIT ) + if ( pfn >= max_page ) { - switch ( page->flags & PG_type_mask ) + ret = -EINVAL; + } + else + { + page = frame_table + pfn; + + op.u.getpageframeinfo.domain = page->flags & PG_domain_mask; + op.u.getpageframeinfo.type = NONE; + if ( page->type_count & REFCNT_PIN_BIT ) { - case PGT_l1_page_table: - op.u.getpageframeinfo.type = L1TAB; - break; - case PGT_l2_page_table: - op.u.getpageframeinfo.type = L2TAB; - break; + switch ( page->flags & PG_type_mask ) + { + case PGT_l1_page_table: + op.u.getpageframeinfo.type = L1TAB; + break; + case PGT_l2_page_table: + op.u.getpageframeinfo.type = L2TAB; + break; + } } - } - copy_to_user(u_dom0_op, &op, sizeof(op)); + copy_to_user(u_dom0_op, &op, sizeof(op)); + } } break; -- 2.30.2